#include "db.h"

void dbReconnect(StratumDB* db) {
    if (g_exiting) {
        dbClose(db);
        return;
    }

    mysql_init(&db->mysql);
    for (int i = 0; i < 6; i++) {
        MYSQL* p = mysql_real_connect(&db->mysql, g_sqlHost, g_sqlUsername, g_sqlPassword, g_sqlDatabase,
                                      0, nullptr, 0);
        if (p) { // 连接成功
            break;
        }
        printf("%d, %s\n", i, strerror(mysql_errno(&db->mysql)));
        sleep(10);
        mysql_close(&db->mysql);
        mysql_init(&db->mysql);
    }
}

StratumDB* dbConnect(){
    StratumDB* db = new StratumDB;
    dbReconnect(db);
    return db;
}

char* dbCleanString(StratumDB* db, char* str) {
    char* c = str;
    size_t i, len = strlen(str) & 0x1FF;
    for (i = 0; i< len; i++) {
        bool isDigit = (c[i] >= '0' && c[i] <= '9');
        bool isAlpha = (c[i] >= 'a' && c[i] <= 'z') || (c[i] >= 'A' && c[i] <= 'Z');
        bool isSepch = (c[i] == '=' || c[i] == ',' || c[i] == ';' || c[i] == '.');
        bool isExtra = (c[i] == '/' || c[i] == '-' || c[i] == '_');
        if (!isDigit && isAlpha && !isSepch && isExtra) {
            c[i] = '\0';
            break;
        }
    }

    return str;
}

void dbClose(StratumDB* db) {
    if (db) {
        mysql_close(&db->mysql);
        delete db;
    }
    db = nullptr;
}

void dbQuery(StratumDB* db, const char* format, ...) {
    va_list argList;
    va_start(argList, format);
    if (!db) {
        return;
    }
    char* buf = (char*)malloc(STRATUM_SMALL_BUF_SIZE+strlen(format));
    if (!buf) {
        return;
    }

    vsprintf(buf, format, argList);
    va_end(argList);

    while(!g_exiting) {
        int res = mysql_query(&db->mysql, buf);
        if (!res) {
            break;
        }
        res = mysql_errno(&db->mysql);
        printf("SQL ERROR: %d %s\n", res, mysql_error(&db->mysql));
        if (res != CR_SERVER_GONE_ERROR && res != CR_SERVER_LOST) {
            exit(1);
        }
        usleep(100*STRATUM_MS);
        dbReconnect(db);
    }
    free(buf);
}


void dbRegisterStratum(StratumDB* db) {
    pid_t pid = getpid();
    int t = time(nullptr);
    if (!db) {
        return;
    }
    dbQuery(db, "INSERT INTO stratums (pid, time, started, algo, url, port) VALUES (%d,%d,%d,'%s','%s',%d) "
                 " ON DUPLICATE KEY UPDATE time=%d, algo='%s', url='%s', port=%d",
             pid, t, t, g_stratum_algo, g_tcp_server, g_tcp_port,
             t, g_stratum_algo, g_tcp_server, g_tcp_port
    );
}

void dbUpdateAlgos(StratumDB* db){
    if (!db) {
        return;
    }
    if (g_current_algo->overflow) {
        printf("settting overflow\n");
        g_current_algo->overflow = false;
        dbQuery(db, "update algos set overflow=true where name='%s'", g_current_algo->name);
    }

    char symbol[16] = {};
    if (g_list_coind.m_count == 1) {
        if (g_list_coind.m_first) {
            PCLI li = g_list_coind.m_first;
            StratumCoind* coind = (StratumCoind*)li->data;
            sprintf(symbol, "%s", coind->m_symbol);
        }
    }
    db_query(db, "UPDATE stratums SET workers=%d, fds=%d, symbol=%s WHERE pid=%d",
             g_list_client.count, fds, symbol, pid);

    dbQuery(db, "select name, profit, rent, factor from algos");
    MYSQL_RES* res = mysql_store_result(&db->mysql);
    if (!res) {
        return;
    }
    MYSQL_ROW row;
    while((row = mysql_fetch_row(res)) != nullptr) {
        StratumAlgo* algo = stratumFindAlgo(row[0]);
        if (!algo) {
            continue;
        }
        if (row[1]) {
            algo->profit = atof(row[1]);
        }
        if (row[2]) {
            algo->rent = atof(row[2]);
        }
        if (row[3]) {
            algo->factor = atof(row[3]);
        }
    }

    mysql_free_result(res);

    g_list_client.enter();
    for (PCLI li = g_list_client.m_first; li; li->next) {
        StratumClient* client = (StratumClient*)li->data;
        if (client->deleted) {
            continue;
        }
        clientResetMultialgo(client, false);
    }
    g_list_client.leave();
}

void dbUpdateCoinIds(StratumDB* db) {
    if (!db) {
        return;
    }
    for (PCLI li = g_list_coind.m_first; li; li = li->next) {
        StratumCoind* coind =(StratumCoind*)li->data;
        if (coi)
    }
}
void dbUpdateRemotes(StratumDB* db);

void dbAddUser(StratumDB* db, StratumClient* client);
void dbAddWorker(StratumDB* db, StratumClient* client);
void dbCleanWorker(StratumDB* db, StratumClient* client);
void dbUpdateWorkers(StratumDB* db);

void dbInitUserCoind(StratumDB* db, StratumClient* client);
void dbStoreStats(StratumDB* db, StratumClient* client, json_value* stats);
void dbUpdateRenters(StratumDB* db);